package pt.um.jspecput.lib.writer;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import java.util.ArrayList;

import javax.validation.constraints.Pattern;

import org.databene.benerator.anno.InvocationCount;
import org.databene.feed4junit.Feeder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import antlr.collections.List;

import com.google.common.collect.Lists;

@RunWith(Feeder.class)
public class MethodTest {

    private static Method testMethod;

    @Before
    public void setUp() throws Exception {
        testMethod = new Method();
    }

    @After
    public void tearDown() throws Exception {
        testMethod = null;
    }

    @Test
    public void testMethod() {
        Method m = new Method();

        assertNotNull(m);
    }

    @Test
    public void testMethod_Args() {
        String methodName = "SomeMethod";

        Method m = new Method(methodName);

        assertNotNull(m);
    }

    @Test
    @InvocationCount(10)
    public void testSetGetMethodName(String methodName) {
        testMethod.setMethodName(methodName);

        assertEquals(methodName, testMethod.getMethodName());
    }

    @Test
    public void testSetGetArgumentList() {
        ArrayList<String> args = Lists.newArrayList();

        testMethod.setArgumentList(args);

        assertEquals(args, testMethod.getArgumentList());
    }

    @Test
    public void testSetGetBodyLines() {
        ArrayList<String> lines = Lists.newArrayList();

        testMethod.setBodyLines(lines);

        assertEquals(lines, testMethod.getBodyLines());
    }

    @Test
    public void testHashCode_nullFields() {
        Method m1 = new Method();

        int result = m1.hashCode();

        assertEquals(103823, result);
    }

    @Test
    public void testHashCode_validFields() {
        Method m1 = new Method();
        m1.setMethodName("methodName");

        ArrayList<String> argumentList = Lists.newArrayList();
        argumentList.add("int a");
        argumentList.add("int b");
        m1.setArgumentList(argumentList);

        ArrayList<String> bodyLines = Lists.newArrayList();
        bodyLines.add("int c = a+b");
        bodyLines.add("int d = a*b");
        m1.setBodyLines(bodyLines);

        int result = m1.hashCode();

        assertEquals(1282467946, result);
    }

    @Test
    public void testEquals_sameObject() {
        assertTrue(testMethod.equals(testMethod));
    }

    @Test
    public void testEquals_nullObject() {
        assertFalse(testMethod.equals(null));
    }

    @Test
    public void testEquals_otherObject() {
        assertFalse(testMethod.equals(mock(List.class)));
    }

    @Test
    @InvocationCount(10)
    public void testEquals_methodName(
            @Pattern(regexp = "[a-z][a-zA-Z0-9]*") String methodName) {
        Method m1 = new Method();
        Method m2 = new Method();

        m1.setMethodName(null);
        m2.setMethodName(null);

        assertTrue(m1.equals(m2));
        assertTrue(m2.equals(m1));

        m1.setMethodName(null);
        m2.setMethodName(methodName);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m1));

        m1.setMethodName(methodName);
        m2.setMethodName(null);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m1));

        m1.setMethodName(methodName);
        m2.setMethodName(methodName);

        assertTrue(m1.equals(m2));
        assertTrue(m2.equals(m1));
    }

    @Test
    public void testEquals_argumentList() {
        Method m1 = new Method();
        Method m2 = new Method();
        ArrayList<String> args = Lists.newArrayList();

        m1.setArgumentList(null);
        m2.setArgumentList(null);

        assertTrue(m1.equals(m2));
        assertTrue(m2.equals(m1));

        m1.setArgumentList(null);
        m2.setArgumentList(args);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m1));

        m1.setArgumentList(args);
        m2.setArgumentList(null);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m1));

        m1.setArgumentList(args);
        m2.setArgumentList(args);

        assertTrue(m1.equals(m2));
        assertTrue(m2.equals(m1));
    }

    @Test
    public void testEquals_bodyLines() {
        Method m1 = new Method();
        Method m2 = new Method();
        ArrayList<String> lines = Lists.newArrayList();

        m1.setBodyLines(null);
        m2.setBodyLines(null);

        assertTrue(m1.equals(m2));
        assertTrue(m2.equals(m1));

        m1.setBodyLines(null);
        m2.setBodyLines(lines);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m1));

        m1.setBodyLines(lines);
        m2.setBodyLines(null);

        assertFalse(m1.equals(m2));
        assertFalse(m2.equals(m1));

        m1.setBodyLines(lines);
        m2.setBodyLines(lines);

        assertTrue(m1.equals(m2));
        assertTrue(m2.equals(m1));
    }

    @Test
    @InvocationCount(10)
    public void testToString_noArgs(
            @Pattern(regexp = "[a-z][a-zA-Z0-9]*") String methodName) {
        testMethod.setMethodName(methodName);
        testMethod.setArgumentList(null);

        String expected = "Method: " + methodName + "()";
        assertEquals(expected, testMethod.toString());
    }

    @Test
    @InvocationCount(10)
    public void testToString_OneArg(
            @Pattern(regexp = "[a-z][a-zA-Z0-9]*") String methodName) {
        
        ArrayList<String> args = Lists.newArrayList();
        args.add("int a");

        testMethod.setMethodName(methodName);
        testMethod.setArgumentList(args);

        String expected = "Method: " + methodName + "(int a)";
        assertEquals(expected, testMethod.toString());
    }
    
    @Test
    @InvocationCount(10)
    public void testToString_TwoArgs(
            @Pattern(regexp = "[a-z][a-zA-Z0-9]*") String methodName) {
        
        ArrayList<String> args = Lists.newArrayList();
        args.add("int a");
        args.add("int b");

        testMethod.setMethodName(methodName);
        testMethod.setArgumentList(args);

        String expected = "Method: " + methodName + "(int a, int b)";
        assertEquals(expected, testMethod.toString());
    }
}
